package com.iflyings.android.component

import java.io.Serializable

//CombinedComponentContext只包含left和element两个成员：left用于链接下一个节点，而element就是Element实例也是末端节点
internal class CombinedComponentContext(
    private val left: ComponentContext,
    private val element: ComponentContext.Element
) : ComponentContext, Serializable {

    override fun <T: ComponentContext.Element> first(operation: (T) -> Boolean): T? {
        var cur = this
        while (true) {
            cur.element.first(operation)?.let { return it}
            val next = cur.left
            if (next is CombinedComponentContext) {
                cur = next
            } else {
                return next.first(operation)
            }
        }
    }

    override fun <R> fold(initial: R, operation: (R, ComponentContext.Element) -> R): R =
        operation(left.fold(initial, operation), element)

    override fun minusKey(key: ComponentContext.Key<*>): ComponentContext {
        element[key]?.let { return left } //找到，为当前节点
        val newLeft = left.minusKey(key)
        return when {
            newLeft === left -> this //下一个节点没有改变，可以继续使用
            newLeft === EmptyComponentContext -> element //下一个节点被删除了，就不需要 CombinedComponentContext，直接使用末端节点
            else -> CombinedComponentContext(newLeft, element) //下一个节点变化，需要重新合成
        }
    }

    private fun size(): Int {
        var cur = this
        var size = 2
        while (true) {
            cur = cur.left as? CombinedComponentContext ?: return size
            size++
        }
    }

    private fun contains(element: ComponentContext.Element): Boolean =
        get(element.key) == element

    private fun containsAll(context: CombinedComponentContext): Boolean {
        var cur = context
        while (true) {
            if (!contains(cur.element)) return false
            val next = cur.left
            if (next is CombinedComponentContext) {
                cur = next
            } else {
                return contains(next as ComponentContext.Element)
            }
        }
    }

    override fun equals(other: Any?): Boolean =
        this === other || other is CombinedComponentContext && other.size() == size() && other.containsAll(this)

    override fun hashCode(): Int = left.hashCode() + element.hashCode()

    override fun toString(): String =
        "[" + fold("") { acc, element ->
            if (acc.isEmpty()) element.toString() else "$acc, $element"
        } + "]"

    private fun writeReplace(): Any {
        val n = size()
        val elements = arrayOfNulls<ComponentContext>(n)
        var index = 0
        fold(Unit) { _, element -> elements[index++] = element }
        check(index == n)
        @Suppress("UNCHECKED_CAST")
        return Serialized(elements as Array<ComponentContext>)
    }

    private class Serialized(val elements: Array<ComponentContext>) : Serializable {
        companion object {
            private const val serialVersionUID: Long = 0L
        }

        private fun readResolve(): Any = elements.fold(EmptyComponentContext, ComponentContext::plus)
    }
}